package spark.graphics;

import spark.exception.LoadingException;
import spark.math.Vector2;
import spark.identity.IIdentifiable;

import java.util.HashMap;
import java.util.Collection;
import java.io.IOException;
import java.io.File;

import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.XMLReaderFactory;

public class SpriteMap implements ISpriteMap{
	public static class SpriteData {
		public Vector2 uv = new Vector2();
		public Vector2 dimensions = new Vector2();
		boolean opaque;
	}

	private HashMap<String, SpriteData> spriteDataMap = new HashMap<String, SpriteData>();
	private File theme;
	private IIdentifiable mapTextureId;

	public SpriteMap(String spriteMap, String theme, String themePath) throws LoadingException {
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			SpriteMapParser parser = new SpriteMapParser(theme, spriteDataMap);
			reader.setContentHandler(parser);
			reader.parse(spriteMap);
			this.theme = new File(themePath + parser.getThemeFileName());
			if(!this.theme.exists()) {
				throw new LoadingException("Theme file of theme " + theme + "(" + this.theme.getAbsolutePath() + ") does not exist");
			}
		}
		catch(IOException e) {
			throw new LoadingException(e);
		}
		catch(SAXException e) {
			throw new LoadingException(e);
		}
		Texture themeTexture = Texture.newTexture(this.theme, true);
		for(SpriteData spriteData : spriteDataMap.values()) {
			spriteData.uv.x /= themeTexture.getImageWidth();
			spriteData.uv.y /= themeTexture.getImageHeight();
			spriteData.dimensions.x /= themeTexture.getImageWidth();
			spriteData.dimensions.y /= themeTexture.getImageHeight();
		}
		mapTextureId = themeTexture.getId();
		themeTexture.release();
	}

	@Override
	public Sprite getSprite(String id) {
		SpriteData spriteData = spriteDataMap.get(id);
		if(spriteData == null) {
			throw new SpriteLoadFailedException("No such sprite: " + id);
		}
		try {
			return new Sprite(
					Texture.newTexture(theme, true), spriteData.opaque,
					spriteData.uv, spriteData.dimensions);
		}
		catch(LoadingException e) {
			throw new SpriteLoadFailedException(e);
		}
	}

	@Override
	public HashMap<String, Sprite> all() {
		HashMap<String, Sprite> all = new HashMap<String, Sprite>();
		for(String id : spriteDataMap.keySet()) {
			all.put(id, getSprite(id));
		}
		return all;
	}

	public static Collection<String> allThemes(String spriteMap) throws LoadingException {
		SpriteMapThemeFetcher parser = new SpriteMapThemeFetcher();
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			reader.setContentHandler(parser);
			reader.parse(spriteMap);
		}
		catch(SpriteMapThemeFetcher.FinishedProcessing e) {
			//this is the dirty but apparently only way to stop a sax parser when he has enough data
		}
		catch(IOException e) {
			throw new LoadingException(e);
		}
		catch(SAXException e) {
			throw new LoadingException(e);
		}
		return parser.getThemes();
	}

	@Override
	public IIdentifiable getMapTextureId() {
		return mapTextureId;
	}

	public static class SpriteLoadFailedException extends RuntimeException {
		public SpriteLoadFailedException(String s) {
			super(s);
		}
		public SpriteLoadFailedException(Throwable cause) {
			super(cause);
		}
	}
}
